Unity ECS简介

当前的Unity开发模式

当前的开发方式我们称为”Classic”,是基于Monobehaviours,把代码当成组件的形式关联到gmaeObject上,通常情况下数据和逻辑是紧密的关联在一起的。这种方式可以很快速的进行开发,但对性能会有一定的影响。

Unity正在尝试解决什么样的问题?

基于Unity的GameObject/Monobehaviour机制,可以很简单的为创作游戏编写代码, 但最终往往会让代码陷入难以阅读,维护和优化的境地。

  1. 数据分散在内存的各个地方,想要找到这些数据并传送到cpu会消耗很多性能。

  2. 很多时候会把不需要的数据或者变量一起传送给cpu,这里会有很多浪费的。

  3. 单线程

Entity-Component-System登场

ECS就是为了解决以上的问题而出现的,使用Entity的方式将数据分离出来,然后在需要的地方提供相应的数据,其中System是为了处理Entity中数据的具体逻辑,还有一个很重要的部分”Filter”进行数据的过滤。简单来讲ECS就是数据与数据处理的一个关系。

Entity-Component-System的优点

  1. 高性能
  2. 易编写出高度优化的代码
  3. 易编写出可重用的代码
  4. 数据在内存中是紧密排列的
  5. 和Job System、Burst一起可以获得更高的性能

Job System

大多数使用多线程代码的人都知道编写线程安全代码很难,线程争抢资源可能会发生,但机会非常少,如果程序员没有想到这个问题,可能会导致潜在的严重错误。除此之外,上下文切换的成本很高,因此学习如何平衡工作负载以尽可能高效地运行是很困难的。新的 Unity C# Jobs System将解决这些问题。

如何使用ECS

  1. 目前ECS尚处于开发阶段,只有Unity2018.1版本以上才能开启:
  2. 新建一个项目,设置.NET
    Build Settings - Player Settings ,设置c# runtime:
  3. 导入ECS包
    Window - Package Manager,选择Entities和Hybird Renderer并安装
  4. 新建一个场景,并创建一个Cube,去掉Cube上除了Transform的所有组件,并添加ECS组件,如下图:
  5. 新建HelloRotationSpeedProxy.cs和HelloRotationSpeedSystem.cs脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using Unity.Entities;
using System;

namespace HelloCube.Cube
{
[Serializable]
public struct HelloRotationSpeed : IComponentData
{
public float Value;
}

[UnityEngine.DisallowMultipleComponent]
public class HelloRotationSpeedProxy : ComponentDataProxy<HelloRotationSpeed>
{

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using Unity.Burst;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;

namespace HelloCube.Cube
{

public class HelloRotationSpeedSystem : JobComponentSystem
{
[BurstCompile]
struct HelloRotationSpeedJob : IJobProcessComponentData<Rotation, HelloRotationSpeed>
{
public float dT;

public void Execute(ref Rotation rotation, ref HelloRotationSpeed rotSpeed)
{
rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotSpeed.Value * dT));
}
}

protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var job = new HelloRotationSpeedJob
{
dT = Time.deltaTime
};

return job.Schedule(this, inputDeps);
}
}
}